#ifndef VECT_H
#define VECT_H

#include <fstream>
#include "node.h"
#include <Windows.h>
#include <ShellAPI.h>
#include "errors.h"

/* GLOBAL DECLARATIONS */

size_t i = 0, j = 0 ;

/* --------------------*/

template <class T> class my_vect;

template <class T> std::ofstream &operator<< (std::ofstream &strm, my_vect<T> &ob); 
template <class T> std::ifstream &operator>> (std::ifstream &strm, my_vect<T> &ob);

template <class T> class my_vect
{
private:
	T *dat;       //wskaźnik do tablicy typu T
	size_t ndim;  //ilość elementów, na które pozostała zaalokowana pamięć
	size_t last;  //indeks, który wskazuje na pierwszy “pusty” element tablicy
	void realloc();   //jeśli last >= ndim – zwiększa ndim i realokuje pamiêæ
public:
	my_vect(size_t dim);  
  	virtual ~my_vect();           //zwalnia pamiec
	T * get_begin() { return dat; }   //zwraca wskaźnik do dat[0]
	T * get_end()   { return &dat[last]; }
	void push(const T &ob); // dodaje obiekt typu T w pozycje dat[last] i zwiększa last o jedynkę
	T * pop();       //dekrementuje last i zwraca ostatni element tablicy
	void insert(const T &ob, size_t ind); //wstawia element ob w tablicę odrazu po elemencie dat[ind]
	void insert(const T *ob, size_t ind, size_t numb); // wstawia tablicę elementów ob w tablicę dat odrazu po elemencie dat[ind] numb - ilość elementow w tablice ob
	void erase(size_t id); //usuwa element *ob z tablicy dat i przesuwa elementy tablicy tak, żeby po usunięciu elementy były umieszczone ciągle.(pakuje tablicê)
    void clear_all();
	void edit(const T &ob, size_t id);
    void call_excel();
	void disp();
	void init(size_t dim); //alokuje pamięć dla tablicy dat na dim elementów
	void Prep_excel();
    friend std::ofstream &operator<< <> (std::ofstream &strm, my_vect<T> &ob); 
	friend std::ifstream &operator>> <> (std::ifstream &strm, my_vect<T> &ob);
    T &operator[](const size_t ind);    
};

template <class T> void my_vect<T>::init(size_t dim) { 
    if(!(this->dat)) { 
		try{
			this->dat = new T[dim]; 
			this->last = 0; 
			this->ndim = dim; 
		}
		catch(bad_alloc){
			error(ERR_ALLOC_MEM);
		}
    } 
} 

template <class T> void my_vect<T>::edit(const T &ob, size_t id){
	if(id>=0 && id < this->last){
		this->dat[id] = ob;
	}

}

template <class T> void my_vect<T>::disp(){
		for(i=0; i<last; i++)
			std::cout<<i<<" "<<dat[i]<<std::endl;
}

template <class T> my_vect<T>::my_vect(size_t dim = 0):ndim(dim),last(0),dat(0){
		 this->init(dim);
}

template <class T> void my_vect<T>::clear_all(){
    this->~my_vect(); 
	this->ndim = 0;
	this->last = 0;
}

template <class T> void my_vect<T>::realloc(){
	try{
        this->ndim*=2;
        my_vect<T> *tmp = new my_vect<T>(this->ndim);
		tmp->last = this->last; 
        for(i = 0; i < this->last; i++)
            tmp->dat[i] = this->dat[i];
		this->~my_vect();
        *this = *tmp;
	}
	catch(bad_alloc){
		error(ERR_ALLOC_MEM);
	}
}

template <class T> my_vect<T>::~my_vect(){
    if(this->dat)
        delete [] this->dat;
	this->dat = NULL;
}

template <class T> void my_vect<T>::push(const T &ob){
    if(this->last == this->ndim)
		this->realloc();
	this->dat[this->last++] = ob;
}

template <class T> T * my_vect<T>::pop(){
	if(this->last > 0)
		return this->dat[--this->last];
	return 0;
}

template <class T> void my_vect<T>::insert(const T &ob, size_t ind){
    if(this->last==this->ndim) 
        this->realloc(); 
    if(ind >= this->last) { 
        this->push(ob); 
        return; 
    } 
    for(size_t i = this->last; i > ind; i--) 
        this->dat[i] = this->dat[i-1];  
    this->dat[ind] = ob; 
    this->last++; 
}

template <class T> void my_vect<T>::insert(const T *ob, size_t ind, size_t numb){
    while(this->last+numb>=this->ndim) 
        this->realloc(); 
    this->last+=numb; 
    for(i=this->last-1; i >= ind+numb; i--){
        this->dat[i] = this->dat[i-numb]; 
    } 
    for(i=ind; i < ind+numb; i++) 
        this->dat[i] = *ob++; 
}
template <class T> void my_vect<T>::erase(size_t id){
	try{
        my_vect<T> *tmp = new my_vect<T>(this->ndim);
		tmp->last = --last; 
        for(i = 0, j = 0; i < this->last; i++, j++){
			if(j == id){
				j++;
			}
            tmp->dat[i] = this->dat[j];
		}
		this->~my_vect();
        *this = *tmp;
		}
	catch(bad_alloc){
		error(ERR_ALLOC_MEM);
	}
}

template <class T> std::ofstream &operator<< (std::ofstream &strm, my_vect<T> &ob){
	T *p = ob.get_begin();
	strm.write((char *) &(ob.last), sizeof(size_t));
	for(p; p<ob.get_end(); p++)
		p->writeb(strm);
	return strm;
} 

template <class T> std::ifstream &operator>> (std::ifstream &strm, my_vect<T> &ob){
	ob.clear_all();
	strm.read((char *) &i, sizeof(size_t));
	ob.init(i+1);
	ob.last=i;
	T *p = ob.get_begin();
	for(p; p<ob.get_end(); p++)
		p->readb(strm);
	return strm;
} 

template <class T> T &my_vect<T>::operator[](const size_t ind){
	if(this->last > ind)
		return dat[ind];
	return *((T*)0);
}

template <class T> void my_vect<T>::Prep_excel(){
	std::fstream plik; 
    plik.open("excel.csv", std::ios::out); 
	if(plik.good()){
		for(size_t i=0; i<this->last; i++) 
			plik<<this->dat[i]<<std::endl; 
		plik.close();
	}
	else
		error(ERR_OPEN_FILE);
	string path = ".\\excel.csv";
	ShellExecuteA(GetDesktopWindow(), ("open"), LPCSTR(path.c_str()), NULL, NULL, SW_SHOWNORMAL );
}

#endif // VECT_H
